/**
 * @file    main.c 
 * @author  18408107475@163.com
 * @version V1.0.0
 * @date    2025-07-19
 * @brief   FreeRTOS多任务示例（LED闪烁+按键控制蜂鸣器）
 * @details 
 * 硬件配置：
 *   - 主控芯片：STM32F10x系列（如STM32F103C8T6）
 *   - 外设：
 *     - 绿色LED（PE5）：系统运行状态指示
 *     - 红色LED（PB5）：错误状态指示
 *     - 蜂鸣器：通过GPIO控制（具体引脚由BEEP_STD_Config.h定义）
 *     - 按键KEY1（PE3）：控制蜂鸣器开关（上拉输入，按下为低电平）
 *   - 连接说明：
 *     - LED采用共阳接法（阳极接3.3V，阴极接GPIO，低电平点亮）
 *     - 按键采用上拉输入（默认高电平，按下接地为低电平）
 * 
 * 软件特性：
 *   - 基于FreeRTOS实现多任务并发：
 *     - 绿色LED任务：最高优先级，1Hz周期闪烁
 *     - 红色LED任务：中等优先级，1Hz周期闪烁
 *     - 蜂鸣器任务：最低优先级，按键触发控制
 *   - 任务间通过句柄实现挂起/恢复控制
 *   - 硬件初始化集中管理，提高代码复用性
 */

/* Includes ------------------------------------------------------------------*/
/* Standard includes. */
#include <stdio.h>              // 标准输入输出库（支持printf重定向至串口）

/* Scheduler includes. */
#include "FreeRTOS.h"           // FreeRTOS核心头文件（任务管理基础定义）
#include "task.h"               // FreeRTOS任务管理函数（创建/延时/挂起等）
#include "queue.h"              // FreeRTOS队列头文件（预留，未实际使用）

/* project app includes. */
#include "LED_STD_Config.h"     // LED控制函数（初始化、亮灭状态控制）
#include "BEEP_STD_Config.h"    // 蜂鸣器控制函数（初始化、开关控制）
#include "USART1_Config.h"      // 串口调试功能（波特率配置、格式化输出）
#include "Delay.h"           	// 毫秒级延时函数（非FreeRTOS延时）

/* Library includes. */
#include "stm32f10x.h"          // STM32F10x外设库（GPIO/RCC/NVIC等寄存器定义）

/* Task priorities. */
/* 任务优先级定义：数值越大优先级越高，抢占式调度 */
#define mainGREEN_LED_TASK_PRIORITY   	(tskIDLE_PRIORITY + 3)  // 绿色LED任务优先级（最高）
#define mainRED_LED_TASK_PRIORITY		(tskIDLE_PRIORITY + 2)  // 红色LED任务优先级（中等）
#define mainBEEP_TASK_PRIORITY  		(tskIDLE_PRIORITY + 1)  // 蜂鸣器任务优先级（最低）

/* 按键1硬件配置参数 */
#define KEY1_CLK					RCC_APB2Periph_GPIOE  // KEY1所在GPIO组时钟使能位
#define KEY1_PORT					GPIOE                 // KEY1对应的GPIO端口
#define KEY1_PIN					GPIO_Pin_3            // KEY1对应的引脚号

/*
 * 按键1初始化函数声明（静态函数，仅本文件可见）
 */
static void prvKey1_Config(void);

/*
 * 硬件集中初始化函数声明（静态函数，仅本文件可见）
 */
static void prvHardwareConfig(void);

/* 任务句柄定义 */
TaskHandle_t	Green_LedTaskHandle = NULL;  // 绿色LED任务句柄（用于外部控制该任务）
TaskHandle_t	Red_LedTaskHandle	= NULL;  // 红色LED任务句柄（用于外部控制该任务）

/**
 * @brief  绿色LED闪烁任务函数
 * @param  pvParameters：任务参数（未使用，传入NULL）
 * @note   功能说明：
 *         1. 任务启动时通过串口输出配置完成信息
 *         2. 周期性控制绿色LED亮灭（500ms亮→500ms灭，1Hz频率）
 *         3. LED状态切换操作被临界区保护，防止中断干扰
 *         4. 使用普通延时函数（Delay_ms）实现阻塞，不释放CPU
 *         5. 预留对红色LED任务的恢复操作接口
 */
void vGreen_Led_Task(void *pvParameters)
{
	USART1_Printf("Green Led Task Config!!!\n\r");  // 串口输出任务配置完成信息

	// 任务主循环（FreeRTOS任务必须包含无限循环，防止任务退出）
	for(;;){

		USART1_Printf("Green Led running!!!\n\r");	// 串口输出任务运行状态

		taskENTER_CRITICAL();						// 进入临界区（关闭中断，保护LED操作的原子性）
		Green_Led_ON();               				// 绿色LED点亮（PE5输出低电平，共阳接法）
		USART1_Printf("Green ON!!!\n\r");
		Delay_ms(500);								// 延时500ms（普通延时，不释放CPU使用权）
		Green_Led_OFF();              				// 绿色LED熄灭（PE5输出高电平）
		USART1_Printf("Green OFF!!!\n\r");
		Delay_ms(500);								// 延时500ms（普通延时，任务持续占用CPU）
		taskEXIT_CRITICAL();						// 退出临界区（恢复中断）

		// 可在此处添加对红色LED任务的恢复操作（示例：vTaskResume(Red_LedTaskHandle);）
	}
}

/**
 * @brief  红色LED任务函数
 * @param  pvParameters：任务参数（未使用，传入NULL）
 * @note   功能说明：
 *         1. 任务启动时通过串口输出配置完成信息
 *         2. 周期性控制红色LED亮灭（500ms灭→500ms亮，1Hz频率）
 *         3. 使用FreeRTOS延时函数（vTaskDelay），阻塞时释放CPU
 *         4. 预留自挂起功能，等待绿色LED任务恢复
 */
void vRed_Led_Task(void *pvParameters)
{
	USART1_Printf("Red Led Task Config!!!\n\r");  	// 串口输出任务配置完成信息

	for(;;){
		USART1_Printf("Red Led running!!!\n\r");  	// 串口输出任务运行状态

		Red_Led_OFF();								// 红色LED熄灭（PB5输出高电平，共阳接法）
		USART1_Printf("Red OFF!!!\n\r");
		vTaskDelay(pdMS_TO_TICKS(500));         	// FreeRTOS延时500ms（任务阻塞，释放CPU）
		Red_Led_ON();                           	// 红色LED点亮（PB5输出低电平）
		USART1_Printf("Red ON!!!\n\r");
		vTaskDelay(pdMS_TO_TICKS(500));         	// FreeRTOS延时500ms（任务阻塞，释放CPU）

		// 可在此处添加自挂起功能（示例：vTaskSuspend(NULL); 等待被其他任务恢复）
	}
}

/**
 * @brief  蜂鸣器控制任务函数
 * @param  pvParameters：任务参数（未使用，传入NULL）
 * @note   功能说明：
 *         1. 任务启动时通过串口输出配置完成信息
 *         2. 轮询检测KEY1按键状态（低电平表示按下）
 *         3. 按键按下时开启蜂鸣器，释放时关闭
 *         4. 按键检测包含消抖逻辑（10ms延时循环）
 *         5. 每次循环结束后自我挂起（需外部恢复，否则任务终止）
 */
void vBuzer_Task(void *pvParameters)
{
	USART1_Printf("Buzzer Config!!!\n\r");  				// 串口输出任务配置完成信息

	for(;;){

		USART1_Printf("Buzzer running!!!\n\r");  			// 串口输出任务运行状态
		
		// 检测KEY1是否按下（上拉输入模式，低电平有效）
		if(!GPIO_ReadInputDataBit(KEY1_PORT, KEY1_PIN)){
			Beep_ON();  									// 开启蜂鸣器（对应GPIO输出有效电平）
			
			// 等待按键释放（循环检测，10ms延时实现消抖）
			while(!GPIO_ReadInputDataBit(KEY1_PORT, KEY1_PIN)){
				vTaskDelay(10);  							// 短暂延时，降低CPU占用并消抖
			}
			
			Beep_OFF();  									// 关闭蜂鸣器（对应GPIO输出无效电平）
		}
		
		vTaskSuspend(NULL);  								// 任务自我挂起（需通过vTaskResume恢复运行）
		vTaskDelay(10);      								// 轮询间隔10ms（此语句不会执行，因已挂起）
	}
}

/**
 * @brief  主程序入口
 * @param  None
 * @retval int 程序状态码（实际不会返回，调度器启动后接管系统）
 * @执行流程：
 *   1. 配置NVIC中断优先级分组（抢占优先级4位，响应优先级0位）
 *   2. 调用prvHardwareConfig()完成所有硬件初始化
 *   3. 依次创建绿色LED、蜂鸣器、红色LED三个任务
 *   4. 检查绿色LED任务创建结果，失败则红色LED常亮报警
 *   5. 启动FreeRTOS调度器，开始任务调度
 */
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);  // 配置NVIC优先级分组（FreeRTOS推荐）
	
	prvHardwareConfig();  // 集中初始化所有硬件外设

	BaseType_t 		xReturned;        	// 任务创建返回值（pdPASS表示成功）
	
	// 创建绿色LED闪烁任务
	xReturned = xTaskCreate(
		vGreen_Led_Task,                // 任务函数入口地址
		"Green_Led",                    // 任务名称（调试用，最长16字符）
		configMINIMAL_STACK_SIZE,       // 任务栈大小（最小配置，一般为256字）
		NULL,                        	// 传递给任务的参数（未使用）
		mainGREEN_LED_TASK_PRIORITY,    // 任务优先级（最高）
		&Green_LedTaskHandle           	// 任务句柄输出（用于控制该任务）
	);
	
	// 创建蜂鸣器控制任务（不保存句柄，无需后续控制）
	xTaskCreate(
		vBuzer_Task, 
		"Key control Beep", 
		configMINIMAL_STACK_SIZE, 
		NULL, 
		mainBEEP_TASK_PRIORITY, 
		NULL
	);

	// 创建红色LED任务
	xTaskCreate(
		vRed_Led_Task,
		"Red_Led",
		configMINIMAL_STACK_SIZE,
		NULL,
		mainRED_LED_TASK_PRIORITY,
		&Red_LedTaskHandle
	);

	// 检查绿色LED任务创建结果（失败处理）
	if(xReturned != pdPASS){
		// 任务创建失败：红色LED常亮报警
		for(;;){
			Red_Led_ON();               		// 点亮红色LED（错误状态指示）
			vTaskDelay(pdMS_TO_TICKS(100));		// 延时（调度器未启动时无效，实际常亮）
		}
	}

	// 启动FreeRTOS任务调度器（系统控制权移交内核）
	vTaskStartScheduler();

    // 调度器启动后不会执行到此处（若执行表示内存不足）
    while (1)                  
    {
        // 死循环，防止程序意外退出
    }
}

/**
 * @brief  配置KEY1为上拉输入模式
 * @note   实现细节：
 *         1. 使能KEY1所在GPIO组时钟（GPIOE）
 *         2. 配置PE3为上拉输入模式（内部上拉电阻使能）
 *         3. 输入模式下GPIO_Speed参数无效，仅为结构体完整性设置
 *         4. 硬件逻辑：未按下时高电平，按下时低电平
 */
static void prvKey1_Config(void)
{
	// 使能KEY1所在GPIO组时钟（GPIOE）
	RCC_APB2PeriphClockCmd(KEY1_CLK, ENABLE);

	// 初始化GPIO参数结构体（C99风格初始化）
	GPIO_InitTypeDef GPIO_InitStructure = {
		.GPIO_Mode 	= GPIO_Mode_IPU,      		// 上拉输入模式
		.GPIO_Pin 	= KEY1_PIN,           		// 对应引脚PE3
		.GPIO_Speed = GPIO_Speed_50MHz,   		// 输入模式下无效，默认50MHz
	};

	// 应用配置到指定GPIO组（GPIOE）
	GPIO_Init(KEY1_PORT, &GPIO_InitStructure);
}

/**
 * @brief  系统硬件集中初始化函数
 * @note   初始化顺序：
 *         1. LED硬件（PB5、PE5推挽输出）
 *         2. 蜂鸣器（对应GPIO推挽输出）
 *         3. KEY1按键（PE3上拉输入）
 *         4. USART1（波特率115200，用于调试输出）
 *         集中初始化减少代码冗余，提高维护性
 */
static void prvHardwareConfig(void)
{
	// 初始化LED硬件：配置PB5（红色）、PE5（绿色）为推挽输出
	LED_STD_Config();
	BEEP_STD_Config();  		// 初始化蜂鸣器硬件（配置对应GPIO为推挽输出）
	prvKey1_Config();   		// 初始化KEY1为上拉输入模式
	USART1_Config(115200);  	// 初始化USART1，波特率115200，无奇偶校验
}

/************************ (C) COPYRIGHT  END OF FILE************************/